package com.longma.server.security;

import cn.hutool.core.codec.Base64;
import com.longma.server.constant.enums.ResponseCode;
import com.longma.server.exception.BusinessException;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;

/**
 * @author HIBO
 * @Description JWT(Json Web Token)工具类  创建和解析
 */

@Slf4j
public class JwtTokenUtil {
    /**
     * 请求头
     */
    public static final String AUTH_HEADER_KEY = "Authorization";

    /**
     * Token 前缀
     */
    public static final String TOKEN_PREFIX = "Bearer";

    /**
     * 密钥，经过Base64加密,可自行替换 “number1injiangnanisactually666”
     */
    public static final String BASE64_SECURITY = "bnVtYmVyMWluamlhbmduYW5pc2FjdHVhbGx5NjY2";

    /**
     * JWT配置
     *  audience
     */

    /**
     * 代表这个JWT的接收对象,存入audience
     */
    private static final String CLIENT_ID = "098f6bcd4621d373cade4e832627b4f6";
    /**
     * JWT的签发主体，存入issuer
     */
    private static final String NAME = "restapiuser";
    /**
     * 过期时间，时间戳
     */
    private static final int EXPIRES_SECOND = 17280000; //200min??


    /**
     * 解析jwt
     * @param token
     * @return
     */
    public static Claims parseJWT(String token) {
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(DatatypeConverter.parseBase64Binary(BASE64_SECURITY))
                    .parseClaimsJws(token).getBody();
            return claims;
        } catch (ExpiredJwtException eje) {
            log.error("===== Token过期 =====", eje);
            throw new BusinessException(ResponseCode.TOKEN_EXPIRED);
        } catch (Exception e){
            log.error("===== token解析异常,无效 =====", e);
            throw new BusinessException(ResponseCode.TOKEN_INVALID);
        }
    }

    /**
     * 构建jwt
     * @param userId
     * @param loginName
     * @return
     */
    public static String generateToken(String userId, String loginName, Integer type) {
        try {
            // 使用HS256加密算法
            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

           long nowMillis = System.currentTimeMillis();
            Date now = new Date(nowMillis);

            //生成签名密钥
            byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(BASE64_SECURITY);
            Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

            //添加构成JWT的参数
            JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
                    // 可以将基本不重要的对象信息放到claims
                    .claim("userId", userId)
                    .claim("type", type)
                    .setSubject(loginName)           // 代表这个JWT的主体，即它的所有人
                    .setIssuer(CLIENT_ID)              // 代表这个JWT的签发主体；
                    .setIssuedAt(new Date())        // 是一个时间戳，代表这个JWT的签发时间；
                    .setAudience(NAME)          // 代表这个JWT的接收对象；
                    .signWith(signatureAlgorithm, signingKey);

            //添加Token过期时间
            long expMillis = nowMillis + EXPIRES_SECOND;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp)  // 是一个时间戳，代表这个JWT的过期时间；
                    .setNotBefore(now); // 是一个时间戳，代表这个JWT生效的开始时间，意味着在这个时间之前验证JWT是会失败的

            //生成JWT
            return builder.compact();
        } catch (Exception e) {
            log.error("签名失败", e);
            throw new BusinessException(ResponseCode.SIGNATURE_ERROR);
        }
    }

    /**
     * 从token中获取用户名
     * @param token
     * @return
     */
    public static String getUsernameFromToken(String token){
        return parseJWT(token).getSubject();
    }

    /**
     * 从token中获取用户ID
     * @param token
     * @return
     */
    public static String getUserIdFromToken(String token){
        String userId = parseJWT(token).get("userId", String.class);
        return new String(Base64.decode(userId));
    }


    /**
     * 从token中获取用户ID
     * @param token
     * @return
     */
    public static Integer getUserTypeFromToken(String token){
        Integer type = parseJWT(token).get("type", Integer.class);
        return type;
    }

    /**
     * 是否已过期
     * @param token
     * @return
     */
    public static boolean isTokenExpired(String token)  {
        return parseJWT(token).getExpiration().before(new Date());
    }

    /**
     * 获取过期时间
     * @param token
     * @return Date
     */
    public static Date getExpirationDateFromToken(String token){
        return parseJWT(token).getExpiration();
    }

    /**
     * 验证token是否有效：1、用户名一致  2、未过期
     * @param token
     * @param username
     * @return
     */
    public static boolean validateToken(String token, String username){
        if (username == null) {
            username = "";
        }
        return (username.equals(getUsernameFromToken(token))) && !isTokenExpired(token);
    }
}
